/*
 *
 * Saturn Framework Co.Limt
 * Copyright (c) 2012-2019 tanks, Inc. All rights reserved.
 */
 
package com.saturn.config.server.service.impl;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.saturn.config.server.IConstants;
import com.saturn.config.server.base.entity.AppInfo;
import com.saturn.config.server.base.entity.AppVersion;
import com.saturn.config.server.base.mapper.AppConfigMapper;
import com.saturn.config.server.base.mapper.AppInfoMapper;
import com.saturn.config.server.base.mapper.AppVersionMapper;
import com.saturn.config.server.base.mapper.ConfigLogMapper;
import com.saturn.config.server.service.AppInfoService;
import com.saturn.config.server.util.SignUtil;

import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * <dl>
 *    <dt><b>Title:</b></dt>
 *    <dd>
 *    	none
 *    </dd>
 *    <dt><b>Description:</b></dt>
 *    <dd>
 *    	<p>应用信息表
 *    </dd>
 *
 * @author tanks
 * @date 2019-07-08 15:58:42
 */
@Slf4j
@Service
@SuppressWarnings({ "rawtypes", "unchecked" })
public class AppInfoServiceImpl extends ServiceImpl<AppInfoMapper, AppInfo> implements AppInfoService {

	@Autowired
	private AppConfigMapper appConfigMapper;
	@Autowired
	private AppVersionMapper appVersionMapper;
	@Autowired
	private ConfigLogMapper configLogMapper;
	
	@Override
	@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
	public int saveAddApplication(AppInfo appInfo) {
		QueryWrapper queryWrapper = Wrappers.query();
		queryWrapper.eq("app_name", appInfo.getAppName());
		queryWrapper.eq("env_type", appInfo.getEnvType());
		//从应用复制
		AppInfo entity = this.baseMapper.selectOne(queryWrapper);
		if( entity!=null ) {
			log.info(appInfo.getAppName() + "已经存在，无法新增！");
			return -1;
		}
		Long fromAppId = appInfo.getAppId();
		appInfo.setAppId(null);
		long count = this.baseMapper.insert(appInfo);
		if( count!=1 ) {
			log.info("添加应用失败");
			return -1;
		}
		//从项目复制
		if( fromAppId!=null ) {
			//从项目拷贝属性到创建的项目
			appConfigMapper.copyFromProject(fromAppId, appInfo.getAppId());
		}
		//创建历史版本
		AppVersion appVersion = new AppVersion();
		appVersion.setAppId(String.valueOf(appInfo.getAppId()));
		appVersion.setCreateTime(LocalDateTime.now());
		appVersion.setVersion(appInfo.getCurrentVersion());
		this.appVersionMapper.insert(appVersion);
		return 1;
	}
	
	@Override
	@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
	public int batchSaveApplications(List<AppInfo> appInfos) {
		for(AppInfo appInfo:appInfos) {
			appInfo.setUpdateTime(LocalDateTime.now());
			//检查是否可以更新信息
			AppInfo entity = this.baseMapper.selectById(appInfo.getAppId());
			if( !entity.getAppName().equals(appInfo.getAppName()) || !entity.getAppName().equals(appInfo.getAppName()) ) {
				QueryWrapper queryWrapper = Wrappers.query();
				queryWrapper.eq("app_name", appInfo.getAppName());
				queryWrapper.eq("env_type", appInfo.getEnvType());
				if( this.baseMapper.selectCount(queryWrapper)> 0) {
					//若存在则直接返回失败
					return 0;
				}
			}
		}
		this.updateBatchById(appInfos);
		return appInfos.size();
	}

	@Override
	@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
	public int deleteApplications(String[] appIds) {
		if( appIds==null || appIds.length==0 ) return  0;
		int count = 0;
		for(String appId: appIds) {
			QueryWrapper queryWrapper = Wrappers.query();
			queryWrapper.eq("app_id", appId);
			this.configLogMapper.delete(queryWrapper);
			this.appConfigMapper.delete(queryWrapper);
			this.appVersionMapper.delete(queryWrapper);
			this.baseMapper.deleteById(appId);
			count++;
		}
		return count;
	}
	
	@Override
	@Transactional(readOnly = true, propagation = Propagation.REQUIRED)
	public String genAppliationUrl(String serverUrl,String secret,String[] appIds) {
		if( appIds==null || appIds.length==0 ) return  StrUtil.EMPTY;
		QueryWrapper queryWrapper = Wrappers.query();
		queryWrapper.in("app_id", appIds);
		List<AppInfo> appInfos = this.baseMapper.selectList(queryWrapper);
		if( appInfos==null || appInfos.size()==0 ) return  StrUtil.EMPTY;
		//get public config values
		queryWrapper = Wrappers.query();
		queryWrapper.in("is_public", "1");
		List<AppInfo> pubApps = this.baseMapper.selectList(queryWrapper);
		String pubVersion = pubApps!=null && pubApps.size()>0 ? pubApps.get(0).getCurrentVersion():StrUtil.EMPTY;
		StringBuffer result = new StringBuffer();
		for(AppInfo appInfo:appInfos ) {
			Map<String,Object> params = new HashMap<String,Object>();
			params.put("appName",appInfo.getAppName());
			params.put("envType",appInfo.getEnvType());
			if( "1".equals(appInfo.getIncludePublic()) && StrUtil.isNotBlank(pubVersion) ) {
				params.put("pubVersion",pubVersion);
			}
			params.put("version",appInfo.getCurrentVersion());
			String text = SignUtil.genSignText(params);
			String content = text + "&sign=" + SignUtil.sign(secret, text);
			result.append("#").append(appInfo.getAppName()).append(appInfo.getDescription()).append(IConstants.ENTER_SYMBOL);
			result.append(serverUrl).append(content).append(IConstants.ENTER_SYMBOL);
		}
		return result.toString();
	}
	
	@Override
	@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
	public boolean upgradeApplication(String appId) {
		if( StringUtils.isBlank(appId) ) return  Boolean.FALSE.booleanValue();
		AppInfo appInfo = this.getById(appId);
		if( appInfo==null ) return Boolean.FALSE.booleanValue();
		String[] versions = appInfo.getCurrentVersion().split("\\.");
		int bigVersion = Integer.parseInt(versions[0]);
		int smallVersion = Integer.parseInt(versions[1]);
		smallVersion = smallVersion + 1;
		if( smallVersion==99 ) {
			bigVersion += 1;
			smallVersion = 1;
		}
		String upgradeVersion = String.valueOf(bigVersion) + IConstants.POINT + String.valueOf(smallVersion);
		appInfo.setPreVersion(appInfo.getCurrentVersion());
		appInfo.setCurrentVersion(upgradeVersion);
		appInfo.setUpdateTime(LocalDateTime.now());
		this.updateById(appInfo);
		//版本记录表中增加一条记录
		AppVersion appVersion = new AppVersion();
		appVersion.setAppId(String.valueOf(appInfo.getAppId()));
		appVersion.setCreateTime(LocalDateTime.now());
		appVersion.setVersion(upgradeVersion);
		appVersionMapper.insert(appVersion);
		//App配置表中增加相应的版本记录
		this.appConfigMapper.upgradeVersion(appInfo.getAppId(), appInfo.getPreVersion(), upgradeVersion);
		return Boolean.TRUE.booleanValue();
	}
	
	@Override
	@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
	public boolean downgradeApplication(String appId) {
		if( StringUtils.isBlank(appId) ) return  Boolean.FALSE.booleanValue();
		AppInfo appInfo = this.getById(appId);
		if( appInfo==null ) return Boolean.FALSE.booleanValue();
		String orginVersion = appInfo.getCurrentVersion();
		double targetVersion = Double.parseDouble(appInfo.getCurrentVersion()) - 0.1;
		targetVersion = Double.parseDouble(String.format("%.1f", targetVersion));
		if( targetVersion<1.0 ) {
			log.info("降级版本到" + targetVersion + "，已经超出范围，无法降级!");
			return Boolean.FALSE.booleanValue();
		}
		double preVersion = targetVersion - 0.1f;
		preVersion = Double.parseDouble(String.format("%.1f", preVersion));
		appInfo.setPreVersion(preVersion>=1.0 ? String.format("%.1f", preVersion):StrUtil.EMPTY);
		appInfo.setCurrentVersion(String.format("%.1f", targetVersion));
		appInfo.setUpdateTime(LocalDateTime.now());
		this.updateById(appInfo);
		//版本记录中删除该版本记录
		QueryWrapper queryWrapper = Wrappers.query();
		queryWrapper.eq("app_id", appInfo.getAppId());
		queryWrapper.eq("version", orginVersion);
		appVersionMapper.delete(queryWrapper);
		//App配置表中删除相应的版本记录
		this.appConfigMapper.delete(queryWrapper);
		return Boolean.TRUE.booleanValue();
	}
}
